home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload Trio 2
/
Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO
/
dir24
/
jnos110g.zip
/
PCGEN.ASM
< prev
next >
Wrap
Assembly Source File
|
1994-08-26
|
24KB
|
892 lines
; Collection of assembler support routines for NOS
; Copyright 1991 Phil Karn, KA9Q
;% .MODEL MEMMOD,C
include asmglobal.h
LOCALS
%MACS
.LALL
extrn ctick:proc
public eoi
; Hardware vector for timer linkage
; We use the timer hardware channel here instead of the indirect BIOS
; channel (1ch) because the latter is sluggish when running under DoubleDos
TIMEVEC EQU 08h
.DATA
public Intstk,Stktop,Spsave,Sssave,Mtasker,Hashtab
extrn Isat:word
Spsave dw ? ; Save location for SP during interrupts
Sssave dw ? ; Save location for SS during interrupts
Intstk dw 512 dup(?) ; Interrupt working stack
Stktop equ $ ; SP set here when entering interrupt
Mtasker db ? ; Type of higher multitasker, if any
Hashtab db 256 dup(?) ; Modulus lookup table for iphash()
.CODE
dbase dw @Data
jtable dw l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15
vector dd ? ; place to stach chained vector
vectlo equ word ptr vector
vecthi equ word ptr vector+2
; Re-arm 8259 interrupt controller(s)
; Should be called just after taking an interrupt, instead of just
; before returning. This is because the 8259 inputs are edge triggered, and
; new interrupts arriving during an interrupt service routine might be missed.
eoi proc
cmp Isat,1
jnz @@1 ; Only one 8259, so skip this stuff
mov al,0bh ; read in-service register from
out 0a0h,al ; secondary 8259
nop ; settling delay
nop
nop
in al,0a0h ; get it
or al,al ; Any bits set?
jz @@1 ; nope, not a secondary interrupt
mov al,20h ; Get EOI instruction
out 0a0h,al ; Secondary 8259 (PC/AT only)
@@1: mov al,20h ; 8259 end-of-interrupt command
out 20h,al ; Primary 8259
ret
eoi endp
; Note that all hardware interrupt handlers are expected to return
; the original vector found when the device first attached. We branch
; to it just after we've cleaned up here -- this implements shared
; interrupts through vector chaining. If the original vector isn't
; available, the interrupt handler must return NULL to avoid a crash!
public doretch
label doretch far
cmp ax,0 ; is a chained vector present?
jne @@1 ; yes
if @Datasize NE 0
cmp dx,ax
jne @@1
endif
; common routine for interrupt return
public doret
label doret far
pop es
POPALL
; pop di
; pop si
; pop bp
; pop dx
; pop cx
; pop bx
; pop ax
mov ss,Sssave
mov sp,Spsave ; restore original stack context
pop ds
iret
; Code to handle vector chaining
@@1: mov cs:vectlo,ax ; stash vector for later branch
if @Datasize NE 0
mov cs:vecthi,dx
endif
pop es
POPALL
mov ss,Sssave
mov sp,Spsave ; restore original stack context
pop ds
if @Datasize NE 0
jmp cs:[vector] ; jump to the original interrupt handler
else
jmp cs:[vectlo]
endif
; istate - return current interrupt state
public istate
istate proc
pushf
pop ax
and ax,200h
jnz @@1
ret
@@1: mov ax,1
ret
istate endp
; dirps - disable interrupts and return previous state: 0 = disabled,
; 1 = enabled
public dirps
dirps proc
pushf ; save flags on stack
pop ax ; flags -> ax
and ax,200h ; 1<<9 is IF bit
jz @@1 ; ints are already off; return 0
mov ax,1
cli ; interrupts now off
@@1: ret
dirps endp
; restore - restore interrupt state: 0 = off, nonzero = on
public restore
restore proc
arg is:word
test is,0ffffh
jz @@1
sti
ret
@@1: cli ; should already be off, but just in case...
ret
restore endp
; multitasker types
NONE equ 0
DOUBLEDOS equ 1
DESQVIEW equ 2
WINDOWS3 equ 3
DOS5STYLE equ 4
OS2STYLE equ 5
DPMISTYLE equ 6
; Relinquish processor so other task can run
public giveup
giveup proc
pushf ;save caller's interrupt state
sti ;re-enable interrupts
cmp mtasker, DOUBLEDOS
jnz @@1
mov al,2 ; 110 ms
mov ah,0eeh
int 21h
popf ; restore caller's interrupt state
ret
@@1: cmp mtasker, DESQVIEW
jnz @@2
mov ax, 1000h
int 15h
popf ; restore interrupts
ret
@@2: cmp mtasker, WINDOWS3
je @@4
cmp mtasker, OS2STYLE
je @@4
cmp mtasker, DOS5STYLE
je @@4
cmp mtasker, DPMISTYLE
jne @@3
@@4: mov ax, 1680h ; Release time for DOS 5, OS/2 VDM, Win 3.x
int 2fh
cmp al, 80h ; call supported?
jz @@3 ; nope
popf ; yes - restore interrupts
ret
@@3: hlt ; wait for an interrupt
popf ; restore interrupts
ret
giveup endp
; check for a multitasker running
public chktasker
chktasker proc
mov mtasker,NONE
; Check for Microsoft Windows
mov ax,1600h
int 2fh
test al,7fh ; 00h or 80h means Windows not multitasking
jz @@4
mov mtasker, WINDOWS3
ret
; Check for DoubleDos
@@4: mov ah,0e4h
int 21h
cmp al,1
jz @@1
cmp al,2
jnz @@2
@@1: mov mtasker, DOUBLEDOS
ret
; Check for DESQVIEW
@@2: mov ax, 2b01h
mov cx, 4445h
mov dx, 5351h
int 21h
cmp al, 0ffh
jz @@3
mov mtasker, DESQVIEW
ret
; Check for DOS 5.0 or OS/2 2.0 (N1BEE)
@@3: mov ax, 3306h ;Get true DOS version
xor bx,bx
int 21h
cmp bl, 20 ;DOS 20.x (OS/2 2.0 DOS emulation)
jne @@5
mov mtasker, OS2STYLE
ret
@@5: cmp bl, 5 ;DOS 5.x
je @@5A
cmp bl, 6 ;DOS 6.x
jne @@6
@@5A:
mov mtasker, DOS5STYLE ;Release time same way as Win 3.x
ret
; Handle OS/2 Virtual Boot Machine (N1BEE)
@@6: mov ax, 1687h ;Check DPMI installed state
int 2fh
or ax,ax
jnz @@7
mov mtasker, DPMISTYLE
@@7: ret
chktasker endp
; getss - Read SS for debugging purposes
public getss
getss proc
mov ax,ss
ret
getss endp
; clockbits - Read low order bits of timer 0 (the TOD clock)
; This works only for the 8254 chips used in ATs and 386s.
;
; The timer runs in mode 3 (square wave mode), counting down
; by twos, twice for each cycle. So it is necessary to read back the
; OUTPUT pin to see which half of the cycle we're in. I.e., the OUTPUT
; pin forms the most significant bit of the count. Unfortunately,
; the 8253 in the PC/XT lacks a command to read the OUTPUT pin...
;
; The PC's clock design is soooo brain damaged...
public clockbits
clockbits proc
mov al,0c2h ; latch timer 0 count and status for reading
pushf
cli ; make chip references atomic
out 43h,al ; send latch command
in al,40h ; get status of timer 0
mov bl,al ; save status
in al,40h ; get lsb of count
mov ah,al ; save lsb
in al,40h ; get msb of count
popf ; no more chip references
and bl,80h ; we're only interested in the OUT bit
xchg ah,al ; ax = count in correct order
shr ax,1 ; count /= 2
jz @@3 ; zero count requires carry propagation
@@2: or ah,bl ; combine with OUT bit as most sig bit of count
ret
@@3: xor bl,80h ; propagate carry by toggling OUT bit when cnt == 0
or ah,bl ; combine with !OUT bit as most sig bit of count
ret
clockbits endp
; Internet checksum subroutine
; Compute 1's-complement sum of data buffer
; Uses an unwound loop inspired by "Duff's Device" for performance
;
; Called from C as
; unsigned short
; lcsum(buf,cnt)
; unsigned short *buf;
; unsigned short cnt;
public lcsum
lcsum proc
arg buf:ptr,cnt:word
if @Datasize NE 0
uses ds,si
lds si,buf ; ds:si = buf
else
uses si
mov si,buf ; ds:si = buf (ds already set)
endif
mov cx,cnt ; cx = cnt
cld ; autoincrement si
mov ax,cx
shr cx,1 ; cx /= 16, number of loop iterations
shr cx,1
shr cx,1
shr cx,1
inc cx ; make fencepost adjustment for 1st pass
and ax,15 ; ax = number of words modulo 16
shl ax,1 ; *=2 for word table index
lea bx,jtable ; bx -> branch table
add bx,ax ; index into jump table
clc ; initialize carry = 0
mov dx,0 ; clear accumulated sum
jmp word ptr cs:[bx] ; jump into loop
; Here the real work gets done. The numeric labels on the lodsw instructions
; are the targets for the indirect jump we just made.
;
; Each label corresponds to a possible remainder of (count / 16), while
; the number of times around the loop is determined by the quotient.
;
; The loop iteration count in cx has been incremented by one to adjust for
; the first pass.
;
deloop: lodsw
adc dx,ax
l15: lodsw
adc dx,ax
l14: lodsw
adc dx,ax
l13: lodsw
adc dx,ax
l12: lodsw
adc dx,ax
l11: lodsw
adc dx,ax
l10: lodsw
adc dx,ax
l9: lodsw
adc dx,ax
l8: lodsw
adc dx,ax
l7: lodsw
adc dx,ax
l6: lodsw
adc dx,ax
l5: lodsw
adc dx,ax
l4: lodsw
adc dx,ax
l3: lodsw
adc dx,ax
l2: lodsw
adc dx,ax
l1: lodsw
adc dx,ax
l0: loop deloop ; :-)
adc dx,0 ; get last carries
adc dx,0
mov ax,dx ; result into ax
xchg al,ah ; byte swap result (8088 is little-endian)
ret
lcsum endp
; Link timer handler into timer chain
; Arg == address of timer handler routine
; MUST be called exactly once before uchtimer is called!
toff dw ? ; save location for old vector
tseg dw ? ; must be in code segment
public chtimer
chtimer proc
arg vec:far ptr
uses ds
mov ah,35h ; get current vector
mov al,TIMEVEC
int 21h ; puts vector in es:bx
mov cs:tseg,es ; stash
mov cs:toff,bx
mov ah,25h
mov al,TIMEVEC
lds dx,vec ; ds:si = vec
int 21h ; set new vector
ret
chtimer endp
; unchain timer handler from timer chain
; MUST NOT be called before chtimer!
public uchtimer
uchtimer proc
uses ds
mov ah,25h
mov al,TIMEVEC
mov dx,toff
mov ds,tseg
int 21h ; restore old vector
ret
uchtimer endp
; Clock tick interrupt handler. Note the use of "label" rather than "proc"
; here, necessitated by the fact that "proc" automatically generates BP-saving
; code that we don't want here.
public btick
label btick far
pushf
push ds
cli
mov ds,cs:dbase ; establish interrupt data segment
mov Sssave,ss ; stash user stack context
mov Spsave,sp
mov ss,cs:dbase
lea sp,Stktop
; push ax ; save user regs on interrupt stack
; push bx
; push cx
; push dx
; push bp
; push si
; push di
; push es
PUSHALL
push es
call ctick
pop es
POPALL
; pop di
; pop si
; pop bp
; pop dx
; pop cx
; pop bx
; pop ax
mov ss,Sssave
mov sp,Spsave ; restore original stack context
pop ds
popf
jmp dword ptr [toff] ; link to previous vector
; Convert 32-bit int in network order to host order (dh, dl, ah, al)
; Called from C as
; int32 get32(char *cp);
public get32
get32 proc
arg cp:ptr
if @Datasize NE 0
uses ds,si
lds si,cp ; ds:si = cp
else
uses si
mov si,cp ; ds:si = cp (ds already set)
endif
cld
lodsw
mov dh,al ; high word to dx, a-swapping as we go
mov dl,ah
lodsw
xchg al,ah ; low word stays in ax, just swap
ret
get32 endp
; Convert 16-bit int in network order to host order (ah, al)
; Called from C as
; int16 get16(char *cp);
public get16
get16 proc
arg cp:ptr
if @Datasize NE 0
uses ds,si
lds si,cp ; ds:si = cp
else
uses si
mov si,cp ; ds:si = cp (ds already set)
endif
lodsw ; note: direction flag is don't-care
xchg al,ah ; word stays in ax, just swap
ret
get16 endp
; Convert 32-bit int to network order, returning new pointer
; Called from C as
; char *put32(char *cp,int32 x);
public put32
put32 proc
arg cp:ptr,x:dword
if @Datasize NE 0
uses ds,di
les di,cp ; es:di = cp
mov ax,ss ; our parameter is on the stack, and ds might not
mov ds,ax ; be pointing to ss.
else
uses di
mov di,cp ; es:di = cp
mov ax,ds ; point es at data segment
mov es,ax
endif
cld
mov ax,word ptr (x+2) ; read high word of machine version
xchg ah,al ; swap bytes
stosw ; output in network order
mov ax,word ptr x ; read low word of machine version
xchg ah,al ; swap bytes
stosw ; put in network order
mov ax,di ; return incremented output pointer
if @Datasize NE 0
mov dx,es ; upper half of pointer
endif
ret
put32 endp
; Convert 16-bit int to network order, returning new pointer
; Called from C as
; char *put16(char *cp,int16 x);
public put16
put16 proc
arg cp:ptr,x:word
uses di
if @Datasize NE 0
les di,cp ;es:di = cp
else
mov di,cp ; es:di = cp
mov ax,ds
mov es,ax
endif
cld
mov ax,x ; fetch source word in machine order
xchg ah,al ; swap bytes
stosw ; save in network order
mov ax,di ; return new output pointer to user
if @Datasize NE 0
mov dx,es ; upper half of pointer
endif
ret
put16 endp
; kbraw - raw, nonblocking read from console
; If character is ready, return it; if not, return -1
public kbraw
kbraw proc
mov ah,06h ; Direct Console I/O
mov dl,0ffh ; Read from keyboard
int 21h ; Call DOS
jz @@1 ; zero flag set -> no character ready
mov ah,0 ; valid char is 0-255
ret
@@1: mov ax,-1 ; no char, return -1
ret
kbraw endp
if @CPU AND 2
; fast I/O buffer routines
; version for 80186, 286, 386 (uses ins, outs instructions)
; outbuf - put a buffer to an output port
public outbuf
outbuf proc
arg port:word,buf:ptr,cnt:word
if @Datasize NE 0
uses ds,si
lds si,buf ; ds:si = buf
else
uses si
mov si,buf ;ds:si = buf (ds already set)
endif
mov dx,port
mov cx,cnt
cld
rep outsb ; works only on PC/AT (80286)
mov dx,ds
mov ax,si ; return pointer just past end of buffer
ret
outbuf endp
; inbuf - get a buffer from an input port
public inbuf
inbuf proc
arg port:word,buf:ptr,cnt:word
uses di
if @Datasize NE 0
les di,buf ; es:di = buf
else
mov di,buf ; es:di = buf
mov ax,ds
mov es,ax
endif
mov dx,port
mov cx,cnt
cld
rep insb ; works only on PC/AT (80286)
mov dx,es
mov ax,di ; return pointer just past end of buffer
ret
inbuf endp
else
; fast buffer I/O routines
; version for 8086/8
; outbuf - put a buffer to an output port
public outbuf
outbuf proc
arg port:word,buf:ptr,cnt:word
if @Datasize NE 0
uses ds,si
lds si,buf ; ds:si = buf
else
uses si
mov si,buf ; ds:si = buf (ds already set)
endif
mov dx,port
mov cx,cnt
cld
; If buffer doesn't begin on a word boundary, send the first byte
test si,1 ; (buf & 1) ?
jz @@even ; no
lodsb ; al = *si++;
out dx,al ; out(dx,al);
dec cx ; cx--;
mov cnt,cx ; save for later test
@@even:
shr cx,1 ; cx = cnt >> 1; (convert to word count)
; Do the bulk of the buffer, a word at a time
jcxz @@nobuf ; if(cx != 0){
@@deloop:
lodsw ; do { ax = *si++; (si is word pointer)
out dx,al ; out(dx,lowbyte(ax));
mov al,ah
out dx,al ; out(dx,hibyte(ax));
loop @@deloop ; } while(--cx != 0); }
; now check for odd trailing byte
@@nobuf:
mov cx,cnt
test cx,1
jz @@cnteven
lodsb ; al = *si++;
out dx,al
@@cnteven:
mov dx,ds
mov ax,si ; return pointer just past end of buffer
ret
outbuf endp
; inbuf - get a buffer from an input port
public inbuf
inbuf proc
arg port:word,buf:ptr,cnt:word
uses di
if @Datasize NE 0
les di,buf ; es:di = buf
else
mov di,buf ; es:di = buf
mov ax,ds
mov es,ax
endif
mov dx,port
mov cx,cnt
cld
; If buffer doesn't begin on a word boundary, get the first byte
test di,1 ; if(buf & 1){
jz @@bufeven ;
in al,dx ; al = in(dx);
stosb ; *di++ = al
dec cx ; cx--;
mov cnt,cx ; cnt = cx; } save for later test
@@bufeven:
shr cx,1 ; cx = cnt >> 1; (convert to word count)
; Do the bulk of the buffer, a word at a time
jcxz @@nobuf ; if(cx != 0){
@@deloop:
in al,dx ; do { al = in(dx);
mov ah,al
in al,dx ; ah = in(dx);
xchg al,ah
stosw ; *si++ = ax; (di is word pointer)
loop @@deloop ; } while(--cx != 0);
; now check for odd trailing byte
@@nobuf:
mov cx,cnt
test cx,1
jz @@cnteven
in al,dx
stosb ; *di++ = al
@@cnteven:
mov dx,es
mov ax,di ; return pointer just past end of buffer
ret
inbuf endp
endif
public longdiv
; long unsigned integer division - divide an arbitrary length dividend by
; a 16-bit divisor. Replaces the dividend with the quotient and returns the
; remainder. Called from C as
;
; unsigned short
; longdiv(unsigned short divisor,int cnt,unsigned short *dividend);
;
;Register usage:
; di - divisor
; si - pointer into dividend array
; cx - loop counter, initialized to the number of 16-bit words in the dividend
; ax - low word of current dividend before each divide, current quotient after
; dx - remainder from previous divide carried over, becomes high word of
; dividend for next divide
longdiv proc
arg divisor:word,cnt:word,dividend:ptr
if @Datasize NE 0
uses ds,si,di
lds si,dividend
else
uses si,di
mov si,dividend ;si -> dividend array
endif
cmp divisor,0 ; divisor == 0?
jne @2 ; no, ok
xor ax,ax ; yes, avoid divide-by-zero trap
jmp short @1
@2: mov dx,0 ; init remainder = 0
mov cx,cnt ; init cnt
mov di,divisor ; cache divisor in register
@@deloop:
mov ax,word ptr [si] ; fetch current word of dividend
cmp ax,0 ; dividend == 0 ?
jne @7 ; nope, must do division
cmp dx,0 ; remainder also == 0?
je @4 ; yes, skip division, continue
@7: div di ; do division
mov word ptr [si],ax ; save quotient
@4: inc si ; next word of dividend
inc si
loop @@deloop
mov ax,dx ; return last remainder
@1: ret
longdiv endp
; long unsigned integer multiplication - multiply an arbitrary length
; multiplicand by a 16-bit multiplier, leaving the product in place of
; the multipler, returning the carry. Called from C as
;
; unsigned short
; longmul(unsigned short multiplier,int cnt,unsigned short *multiplier);
;
; Register usage:
; di = multiplier
; si = pointer to current word of multiplicand
; bx = carry from previous round
; cx = count of words in multiplicand
; dx,ax = scratch for multiply
public longmul
longmul proc far
arg multiplier:word,n:word,multiplicand:ptr
if @Datasize NE 0
uses ds,si,di
lds si,multiplicand
else
uses si,di
mov si,multiplicand ; si -> multiplicand array
endif
mov di,multiplier ; cache multiplier in register
xor bx,bx ; init carry = 0
mov cx,n ; fetch n
mov ax,cx
shl ax,1 ; *2 = word offset
add si,ax ; multiplicand += n
@@deloop:
dec si
dec si ; work from right to left
mov ax,word ptr [si] ; fetch current multiplicand
or ax,ax ; skip multiply if zero
jz @@nomult
mul di ; dx:ax <- ax * di
@@nomult:
add ax,bx ; add carry from previous multiply
mov word ptr [si],ax ; save low order word of product
mov bx,0 ; clear previous carry, leaving CF alone
adc bx,dx ; save new carry
xor dx,dx ; clear in case we skip the next mult
loop @@deloop
mov ax,bx ; return final carry
ret
longmul endp
ifdef notdef
; divide 32 bits by 16 bits, returning both quotient and remainder
; This allows C programs that need both to avoid having to do two divisions
;
; Called from C as
; long divrem(dividend,divisor)
; long dividend;
; short divisor;
;
; The quotient is returned in the low 16 bits of the result,
; and the remainder is returned in the high 16 bits.
public divrem
divrem proc
arg dividend:dword,divisor:word
mov ax,word ptr dividend
mov dx,word ptr (dividend+2)
div divisor
ret
divrem endp
endif
; General purpose hash function for IP addresses
; Uses lookup table Hashtab[] initialized in ip.c
; Called from C as
; char hash_ip(int32 ipaddr);
public hash_ip
hash_ip proc
arg ipaddr:dword
lea bx,Hashtab
mov ax,word ptr ipaddr
xor ax,word ptr (ipaddr+2)
xor al,ah
xlat
xor ah,ah
ret
hash_ip endp
; Compute int(log2(x))
; Called from C as
; int log2(int16 x);
public log2
log2 proc
arg x:word
mov cx,16
mov ax,x
@@2: rcl ax,1
jc @@1
loop @@2
@@1: dec cx
mov ax,cx
ret
log2 endp
end